package client_agent

import (
	"lark/util"
	"strconv"
	"time"

	"sync"

	"github.com/pkg/errors"
)

type service struct {
	sync.RWMutex
	id      string
	servers []*server
}

var errNoServer = errors.New("no server")

func newService(id string) *service {
	return &service{id: "service-" + id}
}

func (s *service) getServerByload() (*server, error) {
	var tryNum = 1
try:

	var targets []int
	var minload float64 = 1e10
	var weights []int

	for i, server := range s.servers {
		server.Lock()
		status := server.status
		server.Unlock()

		switch status {
		case newFlag:
			go server.run()

		case closedFlag:
			s.servers[i] = newServer(server.raddr, server.workerNum)
			go s.servers[i].run()

		case prepareFlag:
			continue

		case runningFlag:
			server.Lock()
			load := float64(len(server.reqMap)) / float64(server.workerNum)

			if load < minload {
				targets = []int{i}
				weights = []int{server.workerNum}
				minload = load
			} else if load == minload {
				targets = append(targets, i)
				weights = append(weights, server.workerNum)
			} else {
				//pass
			}
			server.Unlock()

		default:
			panic(errors.Errorf("unknown server status. %d", server.status))
		}
	}

	if len(targets) == 0 && tryNum > 0 {
		tryNum--
		time.Sleep(time.Millisecond * 200)
		goto try
	}

	if len(targets) == 0 {
		return nil, errors.Errorf("server not find. %s", s.id)
	}

	//如果有多个负载相同的 server， 将 worker 数量作为权重进行随机
	target := targets[util.RandomIndexByWeights(weights)]
	return s.servers[target], nil
}

func (s *service) getServerByAddr(dstAddr string) (*server, error) {
	for _, server := range s.servers {
		if server.raddr == dstAddr {
			return server, nil
		}
	}

	return nil, errors.Errorf("server not find. %s", s.id)
}

func (s *service) getServer(addr string) (*server, uint64, error) {
	s.Lock()
	defer s.Unlock()

	if len(s.servers) == 0 {
		return nil, 0, errNoServer
	}

	var ser *server
	var err error
	if addr != "" {
		ser, err = s.getServerByAddr(addr)
	} else {
		ser, err = s.getServerByload()
	}

	if err != nil {
		return nil, 0, err
	}

	msgid := ser.generateMsgID()
	ser.incrMsg(msgid)

	return ser, msgid, nil
}

func (s *service) addServer(addr, workerNum string) bool {
	s.Lock()
	defer s.Unlock()

	for _, srv := range s.servers {
		if srv.raddr == addr {
			logger.Errorf("[%s] add repeat server. addr:%s", s.id, addr)
			return false
		}
	}

	logger.Infof("[%s] add server. %s, %s", s.id, addr, workerNum)
	wn, err := strconv.Atoi(workerNum)
	if err != nil {
		logger.Errorf("[%s] add server worker number error. server:%s, worker number:%s", addr, workerNum)
		wn = 200
	}
	ns := newServer(addr, wn)
	s.servers = append(s.servers, ns)
	return true
}

func (s *service) delServer(addr string) bool {
	s.Lock()
	defer s.Unlock()

	for i, srv := range s.servers {
		if srv.raddr == addr {
			logger.Infof("[%s] delete server. addr:%s", s.id, addr)
			srv.close()
			s.servers = append(s.servers[:i], s.servers[i+1:]...)
			return true
		}
	}

	logger.Errorf("[%s] delete unreal server. name:%s, addr:%s", s.id, addr)
	return false
}

func (s *service) delAll() {
	s.Lock()
	defer s.Unlock()

	for _, srv := range s.servers {
		logger.Infof("[%s] delete server by all. name:%s, addr:%s", s.id, srv.raddr)
		srv.close()
	}
}

func (s *service) empty() bool {
	s.RLock()
	defer s.RUnlock()

	return len(s.servers) == 0
}
